/**
*  @class IFileSystem
*
*  @brief
*
*  @author William McVicar
*/

#pragma once

#ifndef __IFileSystem_H_INCLUDED__
#define __IFileSystem_H_INCLUDED__

//  Library Includes

//  Local Includes
#include "Directory.h"

//  Macros

//  Forward Declarations

//  Types
typedef std::vector< Protein::Directory > PATHS;

//  Constants
extern const c8* g_kTEXT_READ_ONLY;
extern const c8* g_kTEXT_WRITE_ONLY;
extern const c8* g_kTEXT_APPEND;
extern const c8* g_kTEXT_READ_AND_WRITE;
extern const c8* g_kTEXT_CREATE_READ_AND_WRITE;
extern const c8* g_kTEXT_READ_AND_APPEND;
extern const c8* g_kBINARY_READ_ONLY;
extern const c8* g_kBINARY_WRITE_ONLY;
extern const c8* g_kBINARY_APPEND;
extern const c8* g_kBINARY_READ_AND_WRITE;
extern const c8* g_kBINARY_CREATE_READ_AND_WRITE;
extern const c8* g_kBINARY_READ_AND_APPEND;
extern const c8* g_kMIXED_READ_AND_WRITE;
extern const c8* g_kMIXED_CREATE_READ_AND_WRITE;
extern const c8* g_kMIXED_READ_AND_APPEND;

//  Prototypes

namespace Protein
{

class IFileSystem
{
	friend class ProteinFramework;

   //Member Functions
public:

	enum eSEEK_ORIGIN
	{
		SEEKORIGIN_FLOOR = -1,

		SEEKBEGINNING,
		SEEKCURRENT_POS,
		SEEKEND,

		SEEKORIGIN_CEIL
	};

	enum eFILESYSTEM_ERROR
	{
		FILESYSTEM_ERROR_FLOOR = -1,

		FILESYSTEM_ERROR_OK,
		FILESYSTEM_ERROR_FILE_NOT_FOUND,
		FILESYSTEM_ERROR_OUT_OF_MEMORY,

		FILESYSTEM_ERROR_CEIL
	};

	/**
	*	This fucntion adds a search directory to the filesystem
	*
	*	@param _krDirectory The search directory to add.
	*	@author William McVicar
	*/
	void AddSearchDirectory( const Directory& _krDirectory );

	/**
	*	This fucntion adds a search directory to the filesystem. Takes
	*	a wchar_t*
	*
	*	@param _krPath The path to the search directory
	*	@param _bAddRecursive Flag to determine whether to build the subdirectories
	*						  into this directory.
	*	@author William McVicar
	*/
	void CreateSearchDirectory( const c16* _krPath, bool _bAddRecursive );

	/**
	*	This fucntion adds a search directory to the filesystem. Takes
	*	a char*
	*
	*	@param _krPath The path to the search directory
	*	@param _bAddRecursive Flag to determine whether to build the subdirectories
	*						  into this directory.
	*	@author William McVicar
	*/
	void CreateSearchDirectory( const c8* _szPath, bool _bAddRecursive );

	/**
	*	This function returns the path to the current
	*	home directory.
	*	@return The path to the current home directory
	*	@author William McVicar
	*/
	const Directory& GetHomeDirectory() const;

	/**
	*	This function returns the path to the current
	*	working directory.
	*	@return The path to the current working directory
	*	@author William McVicar
	*/
	const Directory& GetWorkingDirectory() const;

	/**
	*	This function sets the path to the current
	*	home directory.
	*	@return The path to the new home directory
	*	@author William McVicar
	*/
	void SetHomeDirectory( const c8* _szHomePath );

	/**
	*	This function sets the path to the current
	*	working directory.
	*	@param _szWorkingDirectory The path to the new working directory
	*	@param _bRecursive Whether or not it should add all sub directories
	*	@author William McVicar
	*/
	void SetWorkingDirectory( const c8* _szWorkingDirectory, bool _bRecursive = true );

	/**
	*	This function sets the directory to the current
	*	working directory.
	*	@param The directory object to be the new working directory
	*	@author William McVicar
	*/
	void SetWorkingDirectory( const Directory& _krWorkingDirectory );

	/**
	*	This function checks to see if a file exists in a given directory.
	*	The working directory is specified as the default directory.
	*
	*	@param _szFilename The filename to search for.
	*	@param _pBuffer A buffer to store the file path in
	*	@param _krDirectory A default directory to start searching in.
	*	@return A filesystem error code
	*
	*	@author William McVicar
	*/	
	virtual eFILESYSTEM_ERROR FileExists( const c8* _szFilename, char* _pBuffer, const Directory& _krDirectory = sm_WorkingDirectory, bool _bRecursive = true ) const = 0;

	/**
	*	This function checks to see if a file exists in a given directory.
	*	The working directory is specified as the default directory.
	*
	*	@param _szFilename The filename to search for.
	*	@param _krDirectory A default directory to start searching in.
	*	@param _strFilepath The path to the file or an empty string if its not found
	*	@return A filesystem error code
	*	@author William McVicar
	*/
	eFILESYSTEM_ERROR FileExists( const c8* _szFilename, std::string& _strFilepath, const Directory& _krDirectory = sm_WorkingDirectory, bool _bRecursive = true ) const;

	/**
	*	This function will open a file given the specified filemode.
	*	files can be opened as read only, write only etc. via passing
	*	the following file mode strings.
	*
	*	g_kTEXT_READ_ONLY
	*	g_kTEXT_WRITE_ONLY
	*	g_kTEXT_APPEND
	*	g_kTEXT_READ_AND_WRITE
	*	g_kTEXT_CREATE_READ_AND_WRITE
	*	g_kTEXT_READ_AND_APPEND
	*	g_kBINARY_READ_ONLY
	*	g_kBINARY_WRITE_ONLY
	*	g_kBINARY_APPEND
	*	g_kBINARY_READ_AND_WRITE
	*	g_kBINARY_CREATE_READ_AND_WRITE
	*	g_kBINARY_READ_AND_APPEND
	*	g_kMIXED_READ_AND_WRITE
	*	g_kMIXED_CREATE_READ_AND_WRITE
	*	g_kMIXED_READ_AND_APPEND;
	*
	*	files can be read/written to as text, binary or a mixture of both.
	*
	*	@param _szFilename The name of the file to open.
	*	@param _szFilemode The mode in which the file will be opened.
	*	@param _ppFile A pointer to the file pointer to open.
	*	@return True if the file was found and opened, false if not.
	*	@author William McVicar
	*/
	virtual bool Open( const c8* _szFilename, const c8* _szFileMode, FILE** _ppFile, bool _bAbsolute = false ) = 0;

	/**
	*	This function checks to see if the current offset within the file is
	*	at the end of it.
	*
	*	@param _pFile The file to check
	*	@return true if its eof false if nots
	*	@author William McVicar
	*/
	bool IsEOF( FILE* _pFile );

	/**
	*	This function will close an opened file
	*	@param _pFile the file to close.
	*	@return True if the file closed, false if not
	*	@author William McVicar
	*/
	virtual bool Close( FILE* _pFile ) = 0;

	/**
	*	This function can be used to read data from a file.
	*
	*	@param _pBuffer A buffer to read the data into.
	*	@param _uiSize The size of each element to read from the file.
	*	@param _uiCount The number of elements to read from the file
	*	@param _pFile The file to read from.
	*	@return The number of elements successfully read from the file.
	*	@author William McVicar.
	*/
	virtual i32 Read( u8* _pBuffer, u32 _uiSize, u32 _uiCount, FILE* _pFile ) = 0;

	/**
	*	This function can be used to write data to a file.
	*
	*	@param _pBuffer A buffer the data to write to the file.
	*	@param _uiSize The size of each element to written to the file.
	*	@param _uiCount The number of elements to written to the file
	*	@param _pFile The file to be written to.
	*	@return The number of elements successfully read from the file.
	*	@author William McVicar.
	*/
	virtual i32 Write( u8* _pBuffer, u32 _uiSize, u32 _uiCount, FILE* _pFile ) = 0;

	/**
	*	This function lets you seek to a certain position within a file.
	*	
	*	@param _pFile The file to seek within
	*	@param _uiOffset The offset from the current file position to seek to.
	*					 this is measured in bytes
	*	@param _iOrigin The position to offset from. There are 3 options:
	*							
	*					1) SEEKBEGINNING	  The beginning of the file
	*					2) SEEKCURRENT_POS The current position of _pFile
	*					3) SEEKEND		  The end of the file marked by EOF
	*
	*	@return True upon success, false upon failure
	*	@author William McVicar	
	*/
	virtual bool Seek( FILE* _pFile, u32 _uiOffset, eSEEK_ORIGIN _eOrigin ) = 0;

	/**
	*	This function will reset the offset within a given file to zero
	*	@param _pFile the file to reset
	*	@author William McVicar
	*/
	virtual void Rewind( FILE* _pFile ) = 0; 

	/**
	*	This function gets the offset into a current file
	*
	*	@param _pFile the file to get the offset into
	*	@return The offset in bytes into the current file
	*	@author William McVicar
	*/
	virtual u32 GetOffset( FILE* _pFile ) = 0;

	/**
	*	This function will read formatted string data from a file.
	*
	*	@param _pFile The file to read the formatted string from.
	*	@param _szFormat The format to read the string in.
	*	@param additional arguments. The formatting data.
	*	@return The total number of characters read, a negetive value
	*			is returned if the function failed.
	*	@author William McVicar
	*/
	virtual i32 ReadFormattedString( FILE* _pFile, const c8* _szFormat, ... ) = 0;

	/**
	*	This function will write formatted string data to a file.
	*
	*	@param _pFile The file to write the formatted string to.
	*	@param _szFormat The format to write the string as.
	*	@param additional arguments. The formatting data.
	*	@return The total number of characters written, EOF is returned 
	*			if the function failed.
	*	@author William McVicar
	*/
	virtual i32 WriteFormattedString( FILE* _pFile, const c8* _szFormat, ... ) = 0;

	/**
	*	This function will read a line from the current file
	*
	*	@param _pBuffer The buffer to read the line of data into
	*	@param _pFile The file to read from. The line will be
	*				  read from the current offset into _pFile
	*	@return True upon success, false upon failure
	*	@author William McVicar
	*/
	virtual bool ReadLine( c8* _pBuffer, FILE* _pFile ) = 0;

	/**
	*	This function will read a chunk of data from the current file
	*
	*	@warning This function allocates and frees memory
	*
	*	@param _pBuffer The buffer to read the data into
	*	@param _pFile The file to read from. The chunk will be
	*				  read from the current offset into _pFile
	*	@return True upon success, false upon failure
	*	@author William McVicar
	*/
	virtual bool ReadChunk( c8* _pBuffer, FILE* _pFile, i32 _iNum ) = 0;

	/**
	*	This function will write a string to the current file
	*
	*	@param _pBuffer The buffer to write 
	*	@param _pFile The file to write to. The line will be
	*				  write from the current offset into _pFile
	*	@return True upon success, false upon failure
	*	@author William McVicar
	*/
	virtual bool WriteString( const c8* _szBuffer, FILE* _pFile ) = 0;

	/**
	*	This function will return the size of a file.
	*
	*	@param _pFile The file to check.
	*	@return The size of the file in bytes
	*	@author William McVicar
	*/
	virtual i32 GetFileSize( FILE* _pFile ) = 0;

	/**
	*	This function makes a fourcc code from 4 chars
	*
	*	@param _c0 The first char
	*	@param _c1 The second char
	*	@param _c2 The third char
	*	@param _c3 The fourth char
	*
	*	@return the fourcc code
	*	@author William McVicar
	*/
	static u32 MakeFourCC( c8 _c0, c8 _c1, c8 _c2, c8 _c3 );

	/**
	*	This function will convert a filesystem error into a CString
	*
	*	@return a cstring containing the error
	*	@author William McVicar
	*/
	static const c8* ErrorToString();

protected: 
   IFileSystem();
   virtual ~IFileSystem();

private: 

   //Member Variables
public:

	static const c8*			sm_szPathSeparator;
	static const c16*			sm_wszPathSeparator;
	static const u32			sm_uiMaxPath;

protected: 
	
	static Directory			sm_HomeDirectory;
	static Directory			sm_WorkingDirectory;

	PATHS						m_FileSystemPaths;	

private: 

};

}

#endif //__IFileSystem_H_INCLUDED__
